#include <sys/eventfd.h>
#include <cstdio>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>

#include "EventLoop.h"
#include "CurrentThread.h"
#include "EPollPoller.h"
#include "Channel.h"


__thread EventLoop* t_loopInThisThread = NULL;

int createEventfd() {
    int efd = ::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
    if (efd < 0) {
        printf("eventfd create fail\n");
        fflush(stdout);
        abort();
    }
    return efd;
}

EventLoop::EventLoop()
    : threadId_(CurrentThread::getRealTid()),
        quit_(false),
        callingPendingFunctors_(false),
        wakeupFd_(createEventfd()),
        wakeupChannel_(this, wakeupFd_),
        poller_(this),
        timerQueue_(this)
    {
        if (t_loopInThisThread == NULL) {
            t_loopInThisThread = this;
        } else {
            printf("another EventLoop exist in this thread");
            fflush(stdout);
            abort();
        }

        wakeupChannel_.setReadCallback(std::bind(&EventLoop::handleRead, this));
        wakeupChannel_.enableReading();
    }

EventLoop::~EventLoop() {
    close(wakeupFd_);
    t_loopInThisThread = NULL;
}

bool EventLoop::isInLoopThread() {
    return threadId_ == CurrentThread::getRealTid();
}

void EventLoop::assertInLoopThread() {
    assert(isInLoopThread());
}

void EventLoop::loop() {
    assert(isInLoopThread());

    while (!quit_) {
        activeChannelList_.clear();
        poller_.poll(kPollTimeoutMs, activeChannelList_);
        for (Channel* channel : activeChannelList_) {
            channel->handleEvent();
        }

        doPendingFunctors();
    }
}

void EventLoop::quit() {
    assert(isInLoopThread());
    quit_ = true;
}

void EventLoop::updateChannel(Channel* channel) {
    assert(isInLoopThread());
    poller_.updateChannel(channel);
}

void EventLoop::runInLoop(const Functor& cb) {
    if (isInLoopThread()) {
        cb();
    } else {
        queueInLoop(cb);
    }
}

void EventLoop::queueInLoop(const Functor& cb) {
    {
        MutexGuard lock(mutex_);
        pendingFunctors_.push_back(std::move(cb));
    }
    if (!isInLoopThread() || callingPendingFunctors_) {
        wakeup();
    }
}

void EventLoop::runAt(TimeStamp ts, TimerCallback cb){
    timerQueue_.addTimer(cb, ts, 0);
}

void EventLoop::runAfter(double delay, TimerCallback cb){
    TimeStamp when = addTime(TimeStamp::now(), delay);
    timerQueue_.addTimer(cb, when, 0);
}

void EventLoop::runEvery(double interval, TimerCallback cb){
    TimeStamp when = addTime(TimeStamp::now(), interval);
    timerQueue_.addTimer(cb, when, interval);
}


void EventLoop::doPendingFunctors() {
    assert(isInLoopThread());

    callingPendingFunctors_ = true;
    std::vector<Functor> functors;
    {
        MutexGuard lock(mutex_);
        swap(functors, pendingFunctors_);
    }
    for (Functor& functor : functors) {
        functor();
    }
    callingPendingFunctors_ = false;
}

void EventLoop::wakeup() {
    uint64_t data;
    int ret = write(wakeupFd_, &data, sizeof(data));
    if (ret != sizeof(data)) {
        printf("EventLoop::wakeup fail\n");
        fflush(stdout);
        abort();
    }
}

void EventLoop::handleRead() {
    assert(isInLoopThread());
    
    uint64_t data;
    int ret = read(wakeupFd_, &data, sizeof(data));
    if (ret != sizeof(data)) {
        printf("EventLoop::handleRead fail\n");
        fflush(stdout);
        abort();
    }
}